/*
 -------------------------------------------------------------------------------
 SerLCD.cpp
 
 Initially created by Cody Null on 9/9/11
 
 Modified by Jordan Hochenbaum on 12/5/11 (Version 1.5)
 http://arduino.cc/playground/Code/SerLCD
 
 Modified by Rowland O'Flaherty on 1/15/12 (Version 1.6)
 
 Version 1.6
 -------------------------------------------------------------------------------
 */

//------------------------------------------------------------------------------
// Includes
//------------------------------------------------------------------------------
#include "SerLCD.h"
#include <SoftwareSerial.h>

//------------------------------------------------------------------------------
// Lifecycle
//------------------------------------------------------------------------------
// Contstructor
SerLCD::SerLCD() : SoftwareSerial(2, 2) {
    pinMode(2, OUTPUT); // Default comm pin is 2
	begin(9600);
	_numlines = LCD_2LINE; // Defaults to 16x2 display
	_numchars = LCD_16CHAR;
	_rowoffset = 0;
}

SerLCD::SerLCD(int pin) : SoftwareSerial(pin, pin) {
    pinMode(pin, OUTPUT);
	begin(9600);
	_numlines = LCD_2LINE; // Defaults to 16x2 display
	_numchars = LCD_16CHAR;
	_rowoffset = 0; 
}

// Destructor
SerLCD::~SerLCD() {
    
}

//// Copy constructor
//SerLCD::SerLCD(const SerLCD& srcObj) {
//    copyHelper(srcObj);
//}

// Assignment operator
const SerLCD& SerLCD::operator=(const SerLCD& rhsObj) {
	// Self-assignment check
    if (this == &rhsObj) {
        return (*this);
    }
    
    // Free old memory
    
    // Copy new memory
    copyHelper(rhsObj);
    
    return (*this);
}

// Copy helper function
void SerLCD::copyHelper(const SerLCD& srcObj) {
    _displayfunction = srcObj._displayfunction;
    _displaycontrol = srcObj._displaycontrol;
    _displaymode = srcObj._displaymode;
    _numlines = srcObj._numlines;
    _numchars = srcObj._numchars;
    _rowoffset = srcObj._rowoffset;
}

//------------------------------------------------------------------------------
// Overloaded Operators
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// Public Member Functions
//------------------------------------------------------------------------------
// Set brightness value range 1-30 1=OFF 30=FULL
void SerLCD::setBrightness(int val){
	if(val >= 1 && val <= 30){
		specialCommand(LCD_BACKLIGHT | (val - 1));
	}
}

// Clears screen and returns cursor to home position
void SerLCD::clear(){
	command(LCD_CLEARDISPLAY);
}

// Clears a single line by writing blank spaces then returning
// cursor to beginning of line
void SerLCD::clearLine(int num){
	if(num > 0 && num <= _numlines){
		setCursor(num, 1);
		print("                ");
		setCursor(num, 1);
	}
}

// Moves cursor to the beginning of selected line
void SerLCD::selectLine(int num){
	if(num > 0 && num <= _numlines){
		setCursor(num, 1);
	}
}

// returns cursor to home position
void SerLCD::home(){
	command(LCD_RETURNHOME);
}

// Saves first 2 lines of txt to splash screen memory
void SerLCD::setSplash(){
	specialCommand(LCD_SETSPLASHSCREEN);
}

// Toggles splashscreen on and off
void SerLCD::toggleSplash(){
	specialCommand(LCD_SPLASHTOGGLE);
}

//  This is for text that flows Left to Right
void SerLCD::leftToRight(){
	_displaymode |= LCD_ENTRYLEFT;
	command(LCD_ENTRYMODESET | _displaymode);
}

// This is for text that flows Right to Left
void SerLCD::rightToLeft() {
	_displaymode &= ~LCD_ENTRYLEFT;
	command(LCD_ENTRYMODESET | _displaymode);
}

// Blinking cursor on/off
void SerLCD::blink(){
	_displaycontrol |= LCD_BLINKON;
	command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void SerLCD::noBlink(){
	_displaycontrol &= ~LCD_BLINKON;
	command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Underline cursor on/off
void SerLCD::cursor(){
	_displaycontrol |= LCD_CURSORON;
	command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void SerLCD::noCursor(){
	_displaycontrol &= ~LCD_CURSORON;
	command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Display on/off
void SerLCD::display(){
	_displaycontrol |= LCD_DISPLAYON;
	command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void SerLCD::noDisplay(){
	_displaycontrol &= ~LCD_DISPLAYON;
	command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Set cursor to specific row and col values start at 1 not 0
void SerLCD::setCursor(int row, int col){
	int row_offsets[2][4] = {
		{ 0x00, 0x40, 0x10, 0x50 },
		{ 0x00, 0x40, 0x14, 0x54 }
	};
	if((row > 0 && row < 3) && (col > 0 && col < 17)){
           command(LCD_SETDDRAMADDR | ((col - 1) + row_offsets[_rowoffset][(row - 1)]));
	}
}

// Creates custom characters 8 char limit
// Input values start with 1
void SerLCD::createChar(int location, uint8_t charmap[]){
	location -= 1;
	location &= 0x07;
  for (int i=0; i<8; i++){
    command(LCD_SETCGRAMADDR | (location << 3) | i);
    write(charmap[i]);
  }
}

// Prints custom character
// Input values start with 1
void SerLCD::printCustomChar(int num){
	write((num - 1));
}

//------------------------------------------------------------------------------
// Private Member Functions
//------------------------------------------------------------------------------
// Functions for sending the special command values
void SerLCD::command(uint8_t value){
	write(0xFE);
	write(value);
	delay(5);
}
void SerLCD::specialCommand(uint8_t value){
	write(0x7C);
	write(value);
	delay(5);
}